//
//  main.m
//  07-内存管理
//
//  Created by LL.F on 16/7/12.
//  Copyright © 2016年 LL.F. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Man.h"

int main(int argc, const char * argv[]) {
    /**
     *  内存问题
     */
    
    // 一. 内存溢出: 内存过多导致溢出, 产生Crash
    // 二. 野指针(过度释放): 指针指向未知区域, 即一块被释放的内存
    // 内存管理方式
    // 1.垃圾回收机制: 只有Mac上有
    // 2.MRC(Manual Reference Counting): 手动管理引用计数
    // 3.ARC(Auto Reference Counting): 自动管理引用计数（现在常用）
    // 注意: ARC基于MRC进行管理, 系统帮程序员添加了内存管理的内容. MRC的内存管理机制是引用计数. iOS支持两种内存管理方式:ARC和MRC, 没有垃圾回收机制. OC中有垃圾回收机制, 用于Mac.
    // 遇到retain, copy, alloc需要进行内存管理
    // 例子:
    NSObject *obj = [NSObject new];//不需要进行内存管理, 因为类方法里封装了持有和释放.
    
    /**
     *  内存管理: 引用计数(计数器)
     */
    // 有retain, alloc, copy时需要对引用计数加1, 有release, autorelease时会对引用计数减1
    // 当对象的引用计数为0的时候, 系统会自动调用对应的dealloc方法
    // 例子: 注意: 内存管理时, 一定要在对对象操作完成后再进行释放
    Man *man = [[Man alloc] init];// 引用计数为1
    NSLog(@"当前引用计数为%ld", man.retainCount);//结果为1
    [man release];// 引用计数为0, 自动调用dealloc方法
    NSLog(@"释放后引用计数还是%ld", man.retainCount);//已将释放了, 引用计数不会再改变, 所以结果还是1
    
    // 集合类型, 会对对象进行引用计数的改变
    // addObject 对 对象进行引用计数 + 1
    // removeObject 对 对象引用计数 - 1
    // 例子:
    Man *newMan = [[Man alloc] init];// newMan引用计数为1
    [newMan retain];// newMan引用计数为2
    NSMutableArray *array = [NSMutableArray arrayWithObjects:man, nil];
    // 将对象添加到数组中, 对象newMan引用计数加1
    NSLog(@"向数组中添加后的引用计数%ld", newMan.retainCount);// 结果为3
    // 将对象从数组中移除, 对象newMan引用计数减1
    [array removeAllObjects];
    NSLog(@"向数组中移除后的引用计数%ld", newMan.retainCount);// 结果为2
    
    
    // ARC下的内存管理(语义特性)
    // strong(ARC, 强引用, 引用计数 + 1) weak(弱引用(应用于对象类型), 对象被释放时, 会自动置空)
    // retain(MRC, 强引用, 引用计数 + 1)
    // copy(拷贝, 把对象拷贝一份, 原来的引用计数不变, 新的对象引用计数 + 1)
    // assign(应用于简单的数据类型, 不会产生引用计数)
    // 注意ARC下也可以用retain, 但是现在有区分了就要用strong
    
    /**
     *  协议 三步走
     */
    // 1.声明协议, 声明对应的方法
    // 2.签订协议
    // 3. 实现协议方法
    
    /**
     *  拷贝
     */
    
    // 如果想实现对象的拷贝, 需进行如下操作
    // 1. 签订协议NSCopying
    // 2. 实现协议方法copyWithZone
    Man *superMan = [man copy];
    NSLog(@"拷贝后 = %@, 拷贝前 = %@", superMan, man);// 拷贝后, 地址不一样
    // 字符串的语义设置, 最好用copy, 提高安全性
    
    // setValueforkey 与set object for key 的区别???????????
    
    // KVC Key-Value-Coding 键值编码 间接访问成员的方式
    // 只要是NSObject的子类就包含这些方法
    // 1.valueForKey:
    // 2.setValue: forKey:
    // 3.valueForKeyPath:
    // 4.setValue: forKeyPath:
    superMan.name = @"jhon";
    superMan.sexual = @"女";
    id name = [superMan valueForKey:@"name"];
    NSLog(@"%@", name);
    [superMan setValue:@"jany" forKey:@"name"];
    NSLog(@"%@", superMan.name);
    
    Man *gayMan = [[Man alloc] init];
    gayMan.gayBoy = superMan;
    id boyName = [gayMan valueForKeyPath:@"gayBoy.name"];//通过一个关键字的路径 访问成员的成员
    NSLog(@"******%@", boyName);
    [gayMan release];
    [gayMan setValue:@"李四" forKeyPath:@"gayBoy.name"];
    NSLog(@"%@", gayMan.gayBoy.name);
    
//    NSDictionary *dic = [NSDictionary dictionary];
//    [dic setValue:@"张三" forKey:@"name"];
//    NSLog(@"%@", dic);// 字典不能够KVC
    
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    [dictionary setValue:@"王五" forKey:@"name"];
    NSLog(@"%@", dictionary);
    
    NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:@"1435145", @"id",  @"sxcgvadgdfghdf", @"descriptipn", nil];
    Man *dicMan = [[Man alloc] init];
    [dicMan setValuesForKeysWithDictionary:dic];
    NSLog(@"%@", dicMan.mId);
    
    // 混编模式下编写代码 ->build Phases - Compile Source文件下
    // 如果需要在ARC工程下使用MRC文件,加标记 -fno-objc-arc
    // 在MRC工程下使用ARC文件, 加标记 -fobjc-arc
    
    return 0;
}
